8.3 关键字:using¶
using定义类型别名¶
C++11新标准引入了using关键字来定义类型别名(传统方法是用typedef定义类型别名):
using Dict = std::map<std::string, std::string>;
Dict dict; // dict的类型是map<string, string>
命名空间的using声明¶
1. using声明命名空间内的成员¶
使用命名空间的using声明后,我们使用命名空间内的成员时就无须加上命名空间的前缀了,例如:
#include <iostream>
int main(void) {
using std::cout;
using std::endl;
cout << "tomocat" << endl;
}
// 输出:
tomocat
2. using声明整个命名空间(不推荐)¶
当然,我们也可以用using直接声明整个命名空间,这样使用命名空间内的所有成员都无须加上前缀了,例如:
Tips:不推荐用using声明整个命名空间, 这样会引入该命名空间内的所有成员,如果有和用户代码重名的成员可能引发非预期的错误。
#include <iostream>
int main(void) {
using namespace std; // 不推荐的写法
cout << "tomocat" << endl;
}
3. 头文件不要包含命名空间的using声明¶
在头文件中包含了命名空间的using声明会导致所有引入该头文件的文件都包含这个声明,可能会产生非预期的命名冲突。
派生类改变个别成员的可访问性¶
有时我们需要改变派生类继承的某个名字的访问级别,通过使用using声明可以实现这一目的:
class Base {
public:
std::size_t size() const { return n; }
protected:
std::size_t n;
};
// private继承: 继承而来的成员是私有成员
class SubClass : private Base {
public:
using Base::size;
protected:
using Base::n;
};
因为SubClass使用了私有继承,所以继承而来的成员size和n默认是派生类SubClass的私有成员,然而我们使用using声明语句改变了这些成员的可访问性。改变之后SubClass的用户将可以使用size成员,而SubClass的派生类可以使用n。
Tips:派生类只能为那些它可以访问(非私有成员)的名字提供
using声明。
避免遮掩继承而来的名称¶
Effective C++:Avoid hiding inherited names.
derived class内的名称会遮盖base class内的名称,在public继承下从来没有人希望如此。
为了让被遮掩的名称再见天日,可使用using声明式或转交函数(forwarding funcitons)。
举个例子:
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
};
class Derived : public Base {
public:
virtual void mf1();
void mf3();
void mf4();
};
base class内所有名为mf1和mf3的函数都被derived class内的mf1和mf3函数都被遮掩掉了。从名称查找观点上看,Base::mf1和Base::mf3不再被Derived class继承。
注意上述的遮掩规则即使base class和derived class内的函数有不同的参数类型都适用,而且不论函数是virtual还是non-virtual都适用。
Derived d;
int x;
d,mf1(); // 正确: 调用Derived::mf1
d.mf1(x); // 错误: Derived::mf1遮掩了Base::mf1
d,mf2(); // 正确: 调用Base::mf2
d.mf3(); // 正确: 调用Derived::mf3
d.mf3(x); // 错误: Derived::mf3遮掩了Base::mf3
你可以使用using声明式达成目标:
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
};
class Derived : public Base {
public:
// 让base class内名为mf1和mf3的所有东西都在Derived作用域内都可见并且public
using Base::mf1;
using Base::mf3;
virtual void mf1();
void mf3();
void mf4();
};
值得一提的是,如果Derived以private的方式继承Base,而Derived唯一想继承的是Base::mf1的无参数版本。而using声明会令继承而来的某给定名称的所有同名函数在derived class中都可见。我们可以使用一个简单的转交函数。
class Base {
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
};
class Derived : private Base { // private 继承
public:
// 转交函数(forwarding function)
virtual void mf1() {
Base::mf1();
}
};
Derived d;
int x;
d.mf1(); // 正确, 调用Derived::mf1
d.mf1(x); // 错误: Base::mf1的有参版本被遮掩了
类模板的类型别名¶
C++11新标准允许我们为类模板定义一个类型别名:
// authors的类型是pair<string, string>
template<typename T> using twin = pair<T, T>;
twin<string> authors;
当我们定义一个模板类型别名时,也可以固定一个或多个模板参数:
template <typename T> using partNo = pair<T, unsigned>;
partNo<string> books; // books类型是pair<string, unsigned>
partNo<Student> kids; // kids类型是pair<Student, unsigned>